﻿using System;
using System.Data;
using System.Data.Entity.Infrastructure;
using System.Linq;
using BabyShop.WebSite.Data;
using BabyShop.WebSite.Exts;
using BabyShop.WebSite.Models;
using BabyShop.WebSite.ViewModels;

namespace BabyShop.WebSite.Services
{
    public class BaseService<T> where T : class 
    {
        protected AppDbServices DataServices { get; private set; }

        protected BaseService()
        {
            if(DataServices == null)
                DataServices = new AppDbServices();
        }

        ~BaseService()
        {
            if(DataServices != null)
                DataServices.Dispose();
        }

        public virtual bool Save(T entity, Guid modifiedBy)
        {
            string keyName = entity.GetKeyName<T>();
            Guid keyValue = entity.GetKeyValue<T, Guid>(keyName);
            Type entityType = typeof (T);
            if(keyValue == Guid.Empty) {
                var keyProperty = entityType.GetProperty(keyName);
                if (keyProperty != null)
                    keyProperty.SetValue(entity, Guid.NewGuid(), null);
                var commProperty = entityType.GetProperties().First(p => p.PropertyType == typeof(CommonColumns));
                if (commProperty != null)
                    commProperty.SetValue(entity, new CommonColumns {ModifiedBy = modifiedBy}, null);
                DataServices.Set<T>().Add(entity);
            }
            else {
                T oldEntity = DataServices.Set<T>().FirstOrDefaultByKeyValue(keyValue);
                if (oldEntity == null)
                    return false;
                TrySetProperties(oldEntity, entity);
                var commProperty = entityType.GetProperties().First(p => p.PropertyType == typeof(CommonColumns));
                if (commProperty != null) {
                    var commPropValue = commProperty.GetValue(oldEntity, null) as CommonColumns;
                    if (commPropValue != null) {
                        commPropValue.LastModified = DateTime.Now;
                        commPropValue.ModifiedBy = modifiedBy;
                    }
                }
                DataServices.Entry(oldEntity).State = EntityState.Modified;
            }
            
            return DataServices.SaveChanges() > 0;
        }

        public virtual bool Delete(Guid keyGuid,Guid modifiedBy)
        {
            if (keyGuid == Guid.Empty)
                return false;
            T entity = DataServices.Set<T>().FirstOrDefaultByKeyValue(keyGuid);
            var commProperty = typeof (T).GetProperties().First(p => p.PropertyType == typeof (CommonColumns));
            if(commProperty != null) {
                var commPropertyValue = commProperty.GetValue(entity, null) as CommonColumns;
                if (commPropertyValue != null) {
                    commPropertyValue.IsDelete = true;
                    commPropertyValue.LastModified = DateTime.Now;
                    commPropertyValue.ModifiedBy = modifiedBy;

                    DataServices.Entry(entity).State = EntityState.Modified;
                    return DataServices.SaveChanges() > 0;
                }
            }
            return false;
        }

        public virtual JqGridSeachResult GetJqGridSearchResult(JqGridViewModel jqGridViewModel, string[] searchColumns, params string[] includeColumns)
        {
            DbQuery<T> sourceQuery = DataServices.Set<T>();
            if(includeColumns != null && includeColumns.Length > 0)
                foreach (string includeColumn in includeColumns) {
                    sourceQuery = sourceQuery.Include(includeColumn);
                }
            var jqGridSearchResult = sourceQuery.WhereUsingRules(jqGridViewModel.Filters, jqGridViewModel.Page,
                                                                 jqGridViewModel.Rows, jqGridViewModel.Sidx,
                                                                 jqGridViewModel.Sord, searchColumns);
            return jqGridSearchResult;
        }

        private void TrySetProperties(T oldEntity,T newEntity)
        {
            Type entityType = typeof (T);
            var entityProperties = entityType.GetProperties();

            foreach (var entityProperty in entityProperties) {
                object newEntityPropertyValue = entityProperty.GetValue(newEntity, null);
                if (newEntityPropertyValue != null)
                    entityProperty.SetValue(oldEntity, newEntityPropertyValue, null);
            }
        }
    }
}